package jdklearning.java8.methodhandle;

import org.apache.commons.lang3.RandomUtils;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;


/**
 *
 * 代码逻辑很简单，随机调用两个都实现了 println(Ljava/lang/String)V 方法的类，代码不需要关心 obj 的值是什么类型，只要实现了 println(Ljava/lang/String;)V 方法就能够被调用。看到这里，你肯定会说这不就是 Java 的反射吗？确实，MethodHandle 和 Reflection 实现的功能有太多相似的地方，都是运行时解析方法调用，但他们之间有着本质的区别：
 *
 * MethodHandle 和 Reflection 都可以分派方法调用，但是 MethodHandle 比 Reflection 更强大
 * ，它是模拟字节码层次的方法分派。有兴趣的同学可以对比 MethodHandles.Lookup 提供的findStatic、findVirtual、findSpecial三个方法和 Reflection 的反射调用；
 * MethodHandle 是结合 invokedynamic 指令一起为动态语言服务的，也就是说MethodHandle
 * （更准确的来说是其设计理念）是服务于所有运行在JVM之上的语言，而 Relection 则只是适用 Java 语言本身。
 *
 * @author shenenlu 2021年06月02日 下午18:20:23
 */
public class MethodHandleTest {

    static class ClassA{
        public void println(String s){
            System.out.println("hi " + s + ",I am classA");
        }
    }

    static class ClassB{
        public void println(String s){
            System.out.println("hi " + s + ",I am classB");
        }
    }

    private static MethodHandle getPrintlnMH(Object obj) throws NoSuchMethodException, IllegalAccessException {
        MethodType mt = MethodType.methodType(void.class, String.class);
        return MethodHandles.lookup().findVirtual(obj.getClass(), "println", mt).bindTo(obj);
    }

    public static void main(String[] args) throws Throwable {
        Object obj = RandomUtils.nextInt(0,100) % 2 == 0 ? new ClassA() : new ClassB();
        getPrintlnMH(obj).invokeExact("a");
    }

}
